home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Meeting Pearls 4
/
Meeting Pearls Vol. IV (1996)(GTI - Schatztruhe)[!].iso
/
Pearls
/
dev
/
Oberon
/
OberonV4
/
system1
/
CRT.Mod
(
.txt
)
< prev
next >
Wrap
Oberon Text
|
1996-05-09
|
28KB
|
873 lines
Syntax10.Scn.Fnt
MODULE CRT; (* H.Moessenboeck 17.11.93, Cocol-R Tables *)
IMPORT Texts, Oberon, Sets;
CONST
maxSymbols* = 300; (*max nr of t, nt, and pragmas*)
maxTerminals* = 256; (*max nr of terminals*)
maxNt* = 128; (*max nr of nonterminals*)
maxNodes* = 1500; (*max nr of graph nodes*)
normTrans* = 0; contextTrans* = 1; (*transition codes*)
maxSetNr = 128; (* max. number of symbol sets *)
maxClasses = 50; (* max. number of character classes *)
(* node types *)
t* = 1; pr* = 2; nt* = 3; class* = 4; char* = 5; wt* = 6; any* = 7; eps* = 8; sync* = 9; sem* = 10;
alt* = 11; iter* = 12; opt* = 13;
noSym* = -1;
eofSy* = 0;
(* token kinds *)
classToken* = 0; (*token class*)
litToken* = 1; (*literal (e.g. keyword) not recognized by DFA*)
classLitToken* = 2; (*token class that can also match a literal*)
Name* = ARRAY 16 OF CHAR; (*symbol name*)
Position* = RECORD (*position of stretch of source text*)
beg*: LONGINT; (*start relative to beginning of file*)
len*: INTEGER; (*length*)
col*: INTEGER; (*column number of start position*)
END;
SymbolNode* = RECORD
typ*: INTEGER; (*nt, t, pr, unknown*)
name*: Name; (*symbol name*)
struct*: INTEGER; (*typ = nt: index of 1st node of syntax graph*)
(*typ = t: token kind: literal, class, ...*)
deletable*: BOOLEAN; (*typ = nt: TRUE, if nonterminal is deletable*)
attrPos*: Position; (*position of attributes in source text*)
semPos*: Position; (*typ = pr: pos of sem action in source text*)
(*typ = nt: pos of local decls in source text *)
line*: INTEGER; (*source text line number of item in this node*)
END;
Set* = ARRAY maxTerminals DIV Sets.size OF SET;
GraphNode* = RECORD
typ* : INTEGER; (* nt,sts,wts,char,class,any,eps,sem,sync,alt,iter,opt*)
next*: INTEGER; (* index of successor node *)
(* next < 0: to successor in enclosing structure *)
p1*: INTEGER; (* typ IN {nt, t, wt}: index to symbol list *)
(* typ = any: index to anyset *)
(* typ = sync: index to syncset *)
(* typ = alt: index of 1st node of 1st alternative*)
(* typ IN {iter, opt}: 1st node in subexpression *)
(* typ = char: ordinal character value *)
(* typ = class: index of character class *)
p2*: INTEGER; (* typ = alt: index of 1st node of 2nd alternative*)
(* typ IN {char, class}: transition code *)
pos*: Position; (* typ IN {nt, t, wt}: pos of actual attribs *)
(* typ = sem: pos of sem action in source text. *)
line*: INTEGER; (* source text line number of item in this node *)
END;
MarkList* = ARRAY maxNodes DIV Sets.size OF SET;
FirstSets = ARRAY maxNt OF RECORD
ts: Set; (*terminal symbols*)
ready: BOOLEAN; (*TRUE = ts is complete*)
END;
FollowSets = ARRAY maxNt OF RECORD
ts: Set; (*terminal symbols*)
nts: Set; (*nts whose start set is to be included*)
END;
CharClass = RECORD
name: Name; (*class name*)
set: INTEGER (* ptr to set representing the class*)
END;
SymbolTable = ARRAY maxSymbols OF SymbolNode;
ClassTable = ARRAY maxClasses OF CharClass;
GraphList = ARRAY maxNodes OF GraphNode;
maxSet*: INTEGER; (* index of last set *)
maxT*: INTEGER; (* terminals stored from 0 .. maxT *)
maxP*: INTEGER; (* pragmas stored from maxT+1 .. maxP *)
firstNt*: INTEGER; (* index of first nt: available after CompSymbolSets *)
lastNt*: INTEGER; (* index of last nt: available after CompSymbolSets *)
maxC*: INTEGER; (* index of last character class *)
semDeclPos*: Position; (*position of global semantic declarations*)
importPos*: Position; (*position of imported identifiers*)
ignored*: Set; (* characters ignored by the scanner *)
ignoreCase*: BOOLEAN; (* TRUE: scanner treats lower case as upper case*)
ddt*: ARRAY 10 OF BOOLEAN; (* debug and test switches *)
nNodes*: INTEGER; (* index of last graph node *)
root*: INTEGER; (* index of root node, filled by ATG *)
w: Texts.Writer;
st: SymbolTable;
gn: GraphList;
first: FirstSets; (*first[i] = first symbols of st[i+firstNt]*)
follow: FollowSets; (*follow[i] = followers of st[i+firstNt]*)
chClass: ClassTable; (*character classes*)
set: ARRAY 128 OF Set; (*set[0] reserved for union of all synchronisation sets*)
dummyName: INTEGER; (*for unnamed character classes*)
PROCEDURE ^MovePragmas;
PROCEDURE ^DelNode*(gn: GraphNode): BOOLEAN;
PROCEDURE Str(s: ARRAY OF CHAR);
BEGIN Texts.WriteString(w, s)
END Str;
PROCEDURE NL;
BEGIN Texts.WriteLn(w)
END NL;
PROCEDURE Length(s: ARRAY OF CHAR): INTEGER;
VAR i: INTEGER;
BEGIN
i:=0; WHILE (i < LEN(s)) & (s[i] # 0X) DO INC(i) END;
RETURN i
END Length;
PROCEDURE Restriction(n: INTEGER);
BEGIN
NL; Str("Restriction "); Texts.WriteInt(w, n, 0); NL; Texts.Append(Oberon.Log, w.buf);
HALT(99)
END Restriction;
PROCEDURE ClearMarkList(VAR m: MarkList);
VAR i: INTEGER;
BEGIN
i := 0; WHILE i < maxNodes DIV Sets.size DO m[i] := {}; INC(i) END;
END ClearMarkList;
PROCEDURE GetNode*(gp: INTEGER; VAR n: GraphNode);
BEGIN
n := gn[gp]
END GetNode;
PROCEDURE PutNode*(gp: INTEGER; n: GraphNode);
BEGIN gn[gp] := n
END PutNode;
PROCEDURE DelGraph*(gp: INTEGER): BOOLEAN;
VAR gn: GraphNode;
BEGIN
IF gp = 0 THEN RETURN TRUE END; (*end of graph found*)
GetNode(gp, gn);
RETURN DelNode(gn) & DelGraph(ABS(gn.next));
END DelGraph;
PROCEDURE NewSym*(typ: INTEGER; name: Name; line: INTEGER): INTEGER;
VAR i: INTEGER;
BEGIN
IF maxT + 1 = firstNt THEN Restriction(6)
ELSE
CASE typ OF
| t: INC(maxT); i := maxT
| pr: DEC(maxP); DEC(firstNt); DEC(lastNt); i := maxP
| nt: DEC(firstNt); i := firstNt
END;
IF maxT >= maxTerminals THEN Restriction(6) END;
st[i].typ := typ; st[i].name := name;
st[i].struct := 0; st[i].deletable := FALSE;
st[i].attrPos.beg := -1;
st[i].semPos.beg := -1;
st[i].line := line
END;
RETURN i
END NewSym;
PROCEDURE GetSym*(sp: INTEGER; VAR sn: SymbolNode);
BEGIN sn := st[sp]
END GetSym;
PROCEDURE PutSym*(sp: INTEGER; sn: SymbolNode);
BEGIN st[sp] := sn
END PutSym;
PROCEDURE FindSym*(name: Name): INTEGER;
VAR i: INTEGER;
BEGIN
i := 0; (*search in terminal list*)
WHILE (i <= maxT) & (st[i].name # name) DO INC(i) END;
IF i <= maxT THEN RETURN i END;
i := firstNt; (*search in nonterminal/pragma list*)
WHILE (i < maxSymbols) & (st[i].name # name) DO INC(i) END;
IF i < maxSymbols THEN RETURN i ELSE RETURN noSym END
END FindSym;
PROCEDURE NewSet*(s: Set): INTEGER;
BEGIN
INC(maxSet); IF maxSet > maxSetNr THEN Restriction(4) END;
set[maxSet] := s;
RETURN maxSet
END NewSet;
PROCEDURE PrintSet(s: ARRAY OF SET; indent: INTEGER);
CONST maxLineLen = 80;
VAR col, i, len: INTEGER; empty: BOOLEAN; sn: SymbolNode;
BEGIN
i := 0; col := indent; empty := TRUE;
WHILE i <= maxT DO
IF Sets.In(s, i) THEN
empty := FALSE; GetSym(i, sn); len := Length(sn.name);
IF col + len + 2 > maxLineLen THEN
NL; col := 1;
WHILE col < indent DO Texts.Write(w, " "); INC(col) END
END;
Str(sn.name); Str(" ");
INC(col, len + 2)
END;
INC(i)
END;
IF empty THEN Str("-- empty set --") END;
NL; Texts.Append(Oberon.Log, w.buf)
END PrintSet;
PROCEDURE CompFirstSet*(gp: INTEGER; VAR fs: Set);
VAR visited: MarkList;
PROCEDURE CompFirst(gp: INTEGER; VAR fs: Set);
VAR s: Set; gn: GraphNode; sn: SymbolNode;
BEGIN
Sets.Clear(fs);
WHILE (gp # 0) & ~ Sets.In(visited, gp) DO
GetNode(gp, gn); Sets.Incl(visited, gp);
CASE gn.typ OF
| nt:
IF first[gn.p1 - firstNt].ready THEN
Sets.Unite(fs, first[gn.p1 - firstNt].ts);
ELSE
GetSym(gn.p1, sn); CompFirst(sn.struct, s); Sets.Unite(fs, s);
END;
| t, wt: Sets.Incl(fs, gn.p1);
| any: Sets.Unite(fs, set[gn.p1])
| alt, iter, opt:
CompFirst(gn.p1, s); Sets.Unite(fs, s);
IF gn.typ = alt THEN CompFirst(gn.p2, s); Sets.Unite(fs, s) END
ELSE (* eps, sem, sync: nothing *)
END;
IF ~ DelNode(gn) THEN RETURN END;
gp := ABS(gn.next)
END
END CompFirst;
BEGIN (* ComputeFirstSet *)
ClearMarkList(visited);
CompFirst(gp, fs);
IF ddt[3] THEN
NL; Str("ComputeFirstSet: gp = "); Texts.WriteInt(w, gp, 0); NL;
PrintSet(fs, 0);
END;
END CompFirstSet;
PROCEDURE CompFirstSets;
VAR i: INTEGER; sn: SymbolNode;
BEGIN
i := firstNt; WHILE i <= lastNt DO first[i-firstNt].ready := FALSE; INC(i) END;
i := firstNt;
WHILE i <= lastNt DO (* for all nonterminals *)
GetSym(i, sn); CompFirstSet(sn.struct, first[i - firstNt].ts);
first[i - firstNt].ready := TRUE;
INC(i)
END;
END CompFirstSets;
PROCEDURE CompExpected*(gp, sp: INTEGER; VAR exp: Set);
BEGIN
CompFirstSet(gp, exp);
IF DelGraph(gp) THEN Sets.Unite(exp, follow[sp - firstNt].ts) END
END CompExpected;
PROCEDURE CompFollowSets;
VAR sn: SymbolNode; gn: GraphNode; curSy, i, size: INTEGER; visited: MarkList;
PROCEDURE CompFol(gp: INTEGER);
VAR s: Set; gn: GraphNode;
BEGIN
WHILE (gp > 0) & ~ Sets.In(visited, gp) DO
GetNode(gp, gn); Sets.Incl(visited, gp);
IF gn.typ = nt THEN
CompFirstSet(ABS(gn.next), s); Sets.Unite(follow[gn.p1 - firstNt].ts, s);
IF DelGraph(ABS(gn.next)) THEN
Sets.Incl(follow[gn.p1 - firstNt].nts, curSy - firstNt)
END
ELSIF gn.typ IN {opt, iter} THEN CompFol(gn.p1)
ELSIF gn.typ = alt THEN CompFol(gn.p1); CompFol(gn.p2)
END;
gp := gn.next
END
END CompFol;
PROCEDURE Complete(i: INTEGER);
VAR j: INTEGER;
BEGIN
IF Sets.In(visited, i) THEN RETURN END;
Sets.Incl(visited, i);
j := 0;
WHILE j <= lastNt - firstNt DO (* for all nonterminals *)
IF Sets.In(follow[i].nts, j) THEN
Complete(j); Sets.Unite(follow[i].ts, follow[j].ts);
Sets.Excl(follow[i].nts, j)
END;
INC(j)
END;
END Complete;
BEGIN (* CompFollowSets *)
curSy := firstNt; size := (lastNt - firstNt + 2) DIV Sets.size;
WHILE curSy <= lastNt + 1 DO (* also for dummy root nt*)
Sets.Clear(follow[curSy - firstNt].ts);
i := 0; WHILE i <= size DO follow[curSy - firstNt].nts[i] := {}; INC(i) END;
INC(curSy)
END;
curSy := firstNt; (*get direct successors of nonterminals*)
WHILE curSy <= lastNt DO
GetSym(curSy, sn); ClearMarkList(visited); CompFol(sn.struct);
INC(curSy)
END;
CompFol(root); (*curSy=lastNt+1*)
curSy := 0; (*add indirect successors to follow.ts*)
WHILE curSy <= lastNt - firstNt DO
ClearMarkList(visited); Complete(curSy);
INC(curSy);
END;
END CompFollowSets;
PROCEDURE CompAnySets;
VAR curSy, i: INTEGER; sn: SymbolNode;
PROCEDURE LeadingAny(gp: INTEGER; VAR a: GraphNode): BOOLEAN;
VAR gn: GraphNode;
BEGIN
IF gp <= 0 THEN RETURN FALSE END;
GetNode(gp, gn);
IF (gn.typ = any) THEN a := gn; RETURN TRUE
ELSE RETURN (gn.typ = alt) & (LeadingAny(gn.p1, a) OR LeadingAny(gn.p2, a))
OR (gn.typ IN {opt, iter}) & LeadingAny(gn.p1, a)
OR DelNode(gn) & LeadingAny(gn.next, a)
END
END LeadingAny;
PROCEDURE FindAS(gp: INTEGER);
VAR gn, gn2, a: GraphNode; s1, s2: Set; p: INTEGER;
BEGIN
WHILE gp > 0 DO
GetNode(gp, gn);
IF gn.typ IN {opt, iter} THEN
FindAS(gn.p1);
IF LeadingAny(gn.p1, a) THEN
CompFirstSet(ABS(gn.next), s1); Sets.Differ(set[a.p1], s1)
END
ELSIF gn.typ = alt THEN
p := gp; Sets.Clear(s1);
WHILE p # 0 DO
GetNode(p, gn2); FindAS(gn2.p1);
IF LeadingAny(gn2.p1, a) THEN
CompFirstSet(gn2.p2, s2); Sets.Unite(s2, s1); Sets.Differ(set[a.p1], s2)
ELSE
CompFirstSet(gn2.p1, s2); Sets.Unite(s1, s2)
END;
p := gn2.p2
END
END;
gp := gn.next
END
END FindAS;
BEGIN
curSy := firstNt;
WHILE curSy <= lastNt DO (* for all nonterminals *)
GetSym(curSy, sn); FindAS(sn.struct);
INC(curSy)
END CompAnySets;
PROCEDURE CompSyncSets;
VAR curSy, i: INTEGER; sn: SymbolNode; visited: MarkList;
PROCEDURE CompSync(gp: INTEGER);
VAR s: Set; gn: GraphNode;
BEGIN
WHILE (gp > 0) & ~ Sets.In(visited, gp) DO
GetNode(gp, gn); Sets.Incl(visited, gp);
IF gn.typ = sync THEN
CompExpected(ABS(gn.next), curSy, s);
Sets.Incl(s, eofSy); Sets.Unite(set[0], s);
gn.p1 := NewSet(s); PutNode(gp, gn)
ELSIF gn.typ = alt THEN CompSync(gn.p1); CompSync(gn.p2)
ELSIF gn.typ IN {iter, opt} THEN CompSync(gn.p1)
END;
gp := gn.next
END
END CompSync;
BEGIN
curSy := firstNt; ClearMarkList(visited);
WHILE curSy <= lastNt DO
GetSym(curSy, sn); CompSync(sn.struct);
INC(curSy);
END CompSyncSets;
PROCEDURE CompDeletableSymbols*;
VAR changed, del: BOOLEAN; i: INTEGER; sn: SymbolNode;
BEGIN
del := FALSE;
REPEAT
changed := FALSE;
i := firstNt;
WHILE i <= lastNt DO (*for all nonterminals*)
GetSym(i, sn);
IF ~sn.deletable & DelGraph(sn.struct) THEN
sn.deletable := TRUE; PutSym(i, sn); changed := TRUE; del := TRUE
END;
INC(i)
END;
UNTIL ~changed;
i := firstNt; IF del THEN NL END;
WHILE i <= lastNt DO
GetSym(i, sn);
IF sn.deletable THEN Str(" "); Str(sn.name); Str(" deletable"); NL END;
INC(i);
END;
Texts.Append(Oberon.Log, w.buf)
END CompDeletableSymbols;
PROCEDURE CompSymbolSets*;
VAR i: INTEGER; sn: SymbolNode;
BEGIN
i := NewSym(t, "???", 0); (*unknown symbols get code maxT*)
MovePragmas;
CompDeletableSymbols;
CompFirstSets;
CompFollowSets;
CompAnySets;
CompSyncSets;
IF ddt[1] THEN
i := firstNt; Str("First & follow symbols:"); NL;
WHILE i <= lastNt DO (* for all nonterminals *)
GetSym(i, sn); Str(sn.name); NL;
Str("first: "); PrintSet(first[i - firstNt].ts, 10);
Str("follow: "); PrintSet(follow[i - firstNt].ts, 10);
NL;
INC(i);
END;
IF maxSet >= 0 THEN NL; NL; Str("List of sets (ANY, SYNC): "); NL END;
i := 0;
WHILE i <= maxSet DO
Str(" set["); Texts.WriteInt (w, i, 2); Str("] = "); PrintSet(set[i], 16);
INC (i)
END;
NL; NL; Texts.Append(Oberon.Log, w.buf)
END;
END CompSymbolSets;
PROCEDURE GetFirstSet(sp: INTEGER; VAR s: Set);
BEGIN s := first[sp - firstNt].ts
END GetFirstSet;
PROCEDURE GetFollowSet(sp: INTEGER; VAR s: Set);
BEGIN s := follow[sp - firstNt].ts
END GetFollowSet;
PROCEDURE GetSet*(nr: INTEGER; VAR s: Set);
BEGIN s := set[nr]
END GetSet;
PROCEDURE MovePragmas;
VAR i: INTEGER;
BEGIN
IF maxP > firstNt THEN
i := maxSymbols - 1; maxP := maxT;
WHILE i > lastNt DO
INC(maxP); IF maxP >= firstNt THEN Restriction(6) END;
st[maxP] := st[i]; DEC(i)
END;
END MovePragmas;
PROCEDURE PrintSymbolTable*;
VAR i, j: INTEGER;
PROCEDURE WriteTyp(typ: INTEGER);
BEGIN
CASE typ OF
| t : Str(" t ");
| pr : Str(" pr ");
| nt : Str(" nt ");
END;
END WriteTyp;
BEGIN (* PrintSymbolTable *)
Str("Symbol Table:"); NL; NL;
Str("nr name typ hasAttribs struct del line"); NL; NL;
i := 0;
WHILE i < maxSymbols DO
Texts.WriteInt(w, i, 3); Str(" ");
j := 0; WHILE (j < 8) & (st[i].name[j] # 0X) DO Texts.Write(w, st[i].name[j]); INC(j) END;
WHILE j < 8 DO Texts.Write(w, " "); INC(j) END;
WriteTyp(st[i].typ);
IF st[i].attrPos.beg >= 0 THEN Str(" TRUE ") ELSE Str(" FALSE") END;
Texts.WriteInt(w, st[i].struct, 10);
IF st[i].deletable THEN Str(" TRUE ") ELSE Str(" FALSE") END;
Texts.WriteInt(w, st[i].line, 6); NL;
IF i = maxT THEN i := firstNt ELSE INC(i) END
END;
NL; NL; Texts.Append(Oberon.Log, w.buf)
END PrintSymbolTable;
PROCEDURE NewClass*(name: Name; set: Set): INTEGER;
BEGIN
INC(maxC); IF maxC >= maxClasses THEN Restriction(7) END;
IF name[0] = "#" THEN name[1] := CHR(ORD("A") + dummyName); INC(dummyName) END;
chClass[maxC].name := name; chClass[maxC].set := NewSet(set);
RETURN maxC
END NewClass;
PROCEDURE ClassWithName*(name: Name): INTEGER;
VAR i: INTEGER;
BEGIN
i := maxC; WHILE (i >= 0) & (chClass[i].name # name) DO DEC(i) END;
RETURN i
END ClassWithName;
PROCEDURE ClassWithSet*(s: Set): INTEGER;
VAR i: INTEGER;
BEGIN
i := maxC; WHILE (i >= 0) & ~ Sets.Equal(set[chClass[i].set], s) DO DEC(i) END;
RETURN i
END ClassWithSet;
PROCEDURE GetClass*(n: INTEGER; VAR s: Set);
BEGIN
GetSet(chClass[n].set, s)
END GetClass;
PROCEDURE GetClassName*(n: INTEGER; VAR name: Name);
BEGIN
name := chClass[n].name
END GetClassName;
PROCEDURE XRef*;
CONST maxLineLen = 80;
TYPE ListPtr = POINTER TO ListNode;
ListNode = RECORD
next: ListPtr;
line: INTEGER;
END;
ListHdr = RECORD
name: Name;
lptr: ListPtr;
END;
VAR gn: GraphNode; col, i, j: INTEGER; l, p, q: ListPtr;
sn: SymbolNode;
xList: ARRAY maxSymbols OF ListHdr;
BEGIN (* XRef *)
IF maxT <= 0 THEN RETURN END;
MovePragmas;
(* initialise cross reference list *)
i := 0;
WHILE i <= lastNt DO (* for all symbols *)
GetSym(i, sn); xList[i].name := sn.name; xList[i].lptr := NIL;
IF i = maxP THEN i := firstNt ELSE INC(i) END
END;
(* search lines where symbol has been referenced *)
i := 1;
WHILE i <= nNodes DO (* for all graph nodes *)
GetNode(i, gn);
IF gn.typ IN {t, wt, nt} THEN
NEW(l); l^.next := xList[gn.p1].lptr; l^.line := gn.line;
xList[gn.p1].lptr := l
END;
INC(i);
END;
(* search lines where symbol has been defined and insert in order *)
i := 1;
WHILE i <= lastNt DO (*for all symbols*)
GetSym(i, sn); p := xList[i].lptr; q := NIL;
WHILE (p # NIL) & (p^.line > sn.line) DO q := p; p := p^.next END;
NEW(l); l^.next := p;
l^.line := -sn.line;
IF q # NIL THEN q^.next := l ELSE xList[i].lptr := l END;
IF i = maxP THEN i := firstNt ELSE INC(i) END
END;
(* print cross reference listing *)
NL; Str("Cross reference list:"); NL; NL; Str("Terminals:"); NL; Str(" 0 EOF"); NL;
i := 1;
WHILE i <= lastNt DO (*for all symbols*)
Texts.WriteInt(w, i, 3); Str(" ");
j := 0; WHILE (j < 15) & (xList[i].name[j] # 0X) DO Texts.Write(w, xList[i].name[j]); INC(j) END;
l := xList[i].lptr; col := 25;
WHILE l # NIL DO
IF col + 5 > maxLineLen THEN
NL; col := 0; WHILE col < 25 DO Texts.Write(w, " "); INC(col) END
END;
IF l^.line = 0 THEN Str("undef") ELSE Texts.WriteInt(w, l^.line, 5) END;
INC(col, 5);
l := l^.next
END;
NL;
IF i = maxT THEN NL; Str("Pragmas:"); NL END;
IF i = maxP THEN NL; Str("Nonterminals:"); NL; i := firstNt ELSE INC(i) END
END;
NL; NL; Texts.Append(Oberon.Log, w.buf)
END XRef;
PROCEDURE NewNode*(typ, p1, line: INTEGER): INTEGER;
BEGIN
INC(nNodes); IF nNodes > maxNodes THEN Restriction(3) END;
gn[nNodes].typ := typ; gn[nNodes].next := 0;
gn[nNodes].p1 := p1; gn[nNodes].p2 := 0;
gn[nNodes].pos.beg := -1; gn[nNodes].line := line;
RETURN nNodes;
END NewNode;
PROCEDURE CompleteGraph*(gp: INTEGER);
VAR p: INTEGER;
BEGIN
WHILE gp # 0 DO
p := gn[gp].next; gn[gp].next := 0; gp := p
END CompleteGraph;
PROCEDURE ConcatAlt*(VAR gL1, gR1: INTEGER; gL2, gR2: INTEGER);
VAR p: INTEGER;
BEGIN
gL2 := NewNode(alt, gL2, 0);
p := gL1; WHILE gn[p].p2 # 0 DO p := gn[p].p2 END; gn[p].p2 := gL2;
p := gR1; WHILE gn[p].next # 0 DO p := gn[p].next END; gn[p].next := gR2
END ConcatAlt;
PROCEDURE ConcatSeq*(VAR gL1, gR1: INTEGER; gL2, gR2: INTEGER);
VAR p, q: INTEGER;
BEGIN
p := gn[gR1].next; gn[gR1].next := gL2; (*head node*)
WHILE p # 0 DO (*substructure*)
q := gn[p].next; gn[p].next := -gL2; p := q
END;
gR1 := gR2
END ConcatSeq;
PROCEDURE MakeFirstAlt*(VAR gL, gR: INTEGER);
BEGIN
gL := NewNode(alt, gL, 0); gn[gL].next := gR; gR := gL
END MakeFirstAlt;
PROCEDURE MakeIteration*(VAR gL, gR: INTEGER);
VAR p, q: INTEGER;
BEGIN
gL := NewNode(iter, gL, 0); p := gR; gR := gL;
WHILE p # 0 DO
q := gn[p].next; gn[p].next := - gL; p := q
END MakeIteration;
PROCEDURE MakeOption*(VAR gL, gR: INTEGER);
BEGIN
gL := NewNode(opt, gL, 0); gn[gL].next := gR; gR := gL
END MakeOption;
PROCEDURE StrToGraph*(str: ARRAY OF CHAR; VAR gL, gR: INTEGER);
VAR len, i: INTEGER;
BEGIN
gR := 0; i := 1; len := Length(str) - 1;
WHILE i < len DO
gn[gR].next := NewNode(char, ORD(str[i]), 0); gR := gn[gR].next;
INC(i)
END;
gL := gn[0].next; gn[0].next := 0
END StrToGraph;
PROCEDURE DelNode*(gn: GraphNode): BOOLEAN;
VAR sn: SymbolNode;
PROCEDURE DelAlt(gp: INTEGER): BOOLEAN;
VAR gn: GraphNode;
BEGIN
IF gp <= 0 THEN RETURN TRUE END; (*end of graph found*)
GetNode(gp, gn);
RETURN DelNode(gn) & DelAlt(gn.next);
END DelAlt;
BEGIN
IF gn.typ = nt THEN GetSym(gn.p1, sn); RETURN sn.deletable
ELSIF gn.typ = alt THEN RETURN DelAlt(gn.p1) OR (gn.p2 # 0) & DelAlt(gn.p2)
ELSE RETURN gn.typ IN {eps, iter, opt, sem, sync}
END DelNode;
PROCEDURE PrintGraph*;
VAR i: INTEGER;
PROCEDURE WriteTyp(typ: INTEGER);
BEGIN
CASE typ OF
| nt : Str("nt ")
| t : Str("t ")
| wt : Str("wt ")
| any : Str("any ")
| eps : Str("eps ")
| sem : Str("sem ")
| sync: Str("sync")
| alt : Str("alt ")
| iter: Str("iter")
| opt : Str("opt ")
ELSE Str("--- ")
END;
END WriteTyp;
BEGIN (* PrintGraph *)
Str("GraphList:"); NL; NL;
Str(" nr typ next p1 p2 line"); NL; NL;
i := 0;
WHILE i <= nNodes DO
Texts.WriteInt(w, i, 3); Str(" ");
WriteTyp(gn[i].typ); Texts.WriteInt(w, gn[i].next, 7);
Texts.WriteInt(w, gn[i].p1, 7);
Texts.WriteInt(w, gn[i].p2, 7);
Texts.WriteInt(w, gn[i].line, 7);
NL;
INC(i);
END;
NL; NL; Texts.Append(Oberon.Log, w.buf)
END PrintGraph;
PROCEDURE FindCircularProductions* (VAR ok: BOOLEAN);
CONST maxList = 150;
TYPE ListEntry = RECORD
left : INTEGER;
right : INTEGER;
deleted: BOOLEAN;
END;
VAR changed, onLeftSide, onRightSide: BOOLEAN; i, j, listLength: INTEGER;
list: ARRAY maxList OF ListEntry;
singles: MarkList;
sn: SymbolNode;
PROCEDURE GetSingles (gp: INTEGER; VAR singles: MarkList);
VAR gn: GraphNode;
BEGIN
IF gp <= 0 THEN RETURN END; (* end of graph found *)
GetNode (gp, gn);
IF gn.typ = nt THEN
IF DelGraph(ABS(gn.next)) THEN Sets.Incl(singles, gn.p1) END
ELSIF gn.typ IN {alt, iter, opt} THEN
IF DelGraph(ABS(gn.next)) THEN
GetSingles(gn.p1, singles);
IF gn.typ = alt THEN GetSingles(gn.p2, singles) END
END
END;
IF DelNode(gn) THEN GetSingles(gn.next, singles) END
END GetSingles;
BEGIN (* FindCircularProductions *)
i := firstNt; listLength := 0;
WHILE i <= lastNt DO (* for all nonterminals i *)
ClearMarkList (singles); GetSym (i, sn);
GetSingles (sn.struct, singles); (* get nt's j such that i-->j *)
j := firstNt;
WHILE j <= lastNt DO (* for all nonterminals j *)
IF Sets.In(singles, j) THEN
list[listLength].left := i; list[listLength].right := j;
list[listLength].deleted := FALSE;
INC (listLength)
END;
INC(j)
END;
INC(i)
END;
REPEAT
i := 0; changed := FALSE;
WHILE i < listLength DO
IF ~ list[i].deleted THEN
j := 0; onLeftSide := FALSE; onRightSide := FALSE;
WHILE j < listLength DO
IF ~ list[j].deleted THEN
IF list[i].left = list[j].right THEN onRightSide := TRUE END;
IF list[j].left = list[i].right THEN onLeftSide := TRUE END
END;
INC(j)
END;
IF ~ onRightSide OR ~ onLeftSide THEN
list[i].deleted := TRUE; changed := TRUE
END
END;
INC(i)
END
UNTIL ~ changed;
i := 0; ok := TRUE;
WHILE i < listLength DO
IF ~ list[i].deleted THEN
ok := FALSE;
GetSym(list[i].left, sn); NL; Str(" "); Str(sn.name); Str(" --> ");
GetSym(list[i].right, sn); Str(sn.name)
END;
INC(i)
END;
Texts.Append(Oberon.Log, w.buf)
END FindCircularProductions;
PROCEDURE LL1Test* (VAR ll1: BOOLEAN);
VAR sn: SymbolNode; curSy: INTEGER;
PROCEDURE LL1Error (cond, ts: INTEGER);
VAR sn: SymbolNode;
BEGIN
ll1 := FALSE;
GetSym (curSy, sn); Str(" LL1 error in "); Str(sn.name); Str(": ");
IF ts > 0 THEN GetSym (ts, sn); Str(sn.name); Str(" is ") END;
CASE cond OF
1: Str(" start of several alternatives.")
| 2: Str(" start & successor of deletable structure")
| 3: Str(" an ANY node that matchs no symbol")
END;
NL; Texts.Append(Oberon.Log, w.buf)
END LL1Error;
PROCEDURE Check (cond: INTEGER; VAR s1, s2: Set);
VAR i: INTEGER;
BEGIN
i := 0;
WHILE i <= maxT DO
IF Sets.In(s1, i) & Sets.In(s2, i) THEN LL1Error(cond, i) END;
INC(i)
END
END Check;
PROCEDURE CheckAlternatives (gp: INTEGER);
VAR gn, gn1: GraphNode; s1, s2: Set; p: INTEGER;
BEGIN
WHILE gp > 0 DO
GetNode(gp, gn);
IF gn.typ = alt THEN
p := gp; Sets.Clear(s1);
WHILE p # 0 DO (*for all alternatives*)
GetNode(p, gn1); CompExpected(gn1.p1, curSy, s2);
Check(1, s1, s2); Sets.Unite(s1, s2);
CheckAlternatives(gn1.p1);
p := gn1.p2
END
ELSIF gn.typ IN {opt, iter} THEN
CompExpected(gn.p1, curSy, s1);
CompExpected(ABS(gn.next), curSy, s2);
Check(2, s1, s2);
CheckAlternatives(gn.p1)
ELSIF gn.typ = any THEN
GetSet(gn.p1, s1);
IF Sets.Empty(s1) THEN LL1Error(3, 0) END (*e.g. {ANY} ANY or [ANY] ANY*)
END;
gp := gn.next
END
END CheckAlternatives;
BEGIN (* LL1Test *)
curSy := firstNt; ll1 := TRUE;
WHILE curSy <= lastNt DO (*for all nonterminals*)
GetSym(curSy, sn); CheckAlternatives (sn.struct);
INC (curSy)
END;
END LL1Test;
PROCEDURE TestCompleteness* (VAR ok: BOOLEAN);
VAR sp: INTEGER; sn: SymbolNode;
BEGIN
sp := firstNt; ok := TRUE;
WHILE sp <= lastNt DO (*for all nonterminals*)
GetSym (sp, sn);
IF sn.struct = 0 THEN
ok := FALSE; NL; Str(" No production for "); Str(sn.name); Texts.Append(Oberon.Log, w.buf)
END;
INC(sp)
END TestCompleteness;
PROCEDURE TestIfAllNtReached* (VAR ok: BOOLEAN);
VAR gn: GraphNode; sp: INTEGER; reached: MarkList; sn: SymbolNode;
PROCEDURE MarkReachedNts (gp: INTEGER);
VAR gn: GraphNode; sn: SymbolNode;
BEGIN
WHILE gp > 0 DO
GetNode(gp, gn);
IF gn.typ = nt THEN
IF ~ Sets.In(reached, gn.p1) THEN (*new nt reached*)
Sets.Incl(reached, gn.p1);
GetSym(gn.p1, sn); MarkReachedNts(sn.struct)
END
ELSIF gn.typ IN {alt, iter, opt} THEN
MarkReachedNts(gn.p1);
IF gn.typ = alt THEN MarkReachedNts(gn.p2) END
END;
gp := gn.next
END
END MarkReachedNts;
BEGIN (* TestIfAllNtReached *)
ClearMarkList(reached);
GetNode(root, gn); Sets.Incl(reached, gn.p1);
GetSym(gn.p1, sn); MarkReachedNts(sn.struct);
sp := firstNt; ok := TRUE;
WHILE sp <= lastNt DO (*for all nonterminals*)
IF ~ Sets.In(reached, sp) THEN
ok := FALSE; GetSym(sp, sn); NL; Str(" "); Str(sn.name); Str(" cannot be reached")
END;
INC(sp)
END;
Texts.Append(Oberon.Log, w.buf)
END TestIfAllNtReached;
PROCEDURE TestIfNtToTerm* (VAR ok: BOOLEAN);
VAR changed: BOOLEAN; gn: GraphNode; sp: INTEGER;
sn: SymbolNode;
termList: MarkList;
PROCEDURE IsTerm (gp: INTEGER): BOOLEAN;
VAR gn: GraphNode;
BEGIN
WHILE gp > 0 DO
GetNode(gp, gn);
IF (gn.typ = nt) & ~ Sets.In(termList, gn.p1)
OR (gn.typ = alt) & ~ IsTerm(gn.p1) & ~ IsTerm(gn.p2) THEN RETURN FALSE
END;
gp := gn.next
END;
RETURN TRUE
END IsTerm;
BEGIN (* TestIfNtToTerm *)
ClearMarkList(termList);
REPEAT
sp := firstNt; changed := FALSE;
WHILE sp <= lastNt DO
IF ~ Sets.In(termList, sp) THEN
GetSym(sp, sn);
IF IsTerm(sn.struct) THEN Sets.Incl(termList, sp); changed := TRUE END
END;
INC(sp)
END
UNTIL ~changed;
sp := firstNt; ok := TRUE;
WHILE sp <= lastNt DO
IF ~ Sets.In(termList, sp) THEN
ok := FALSE; GetSym(sp, sn); NL; Str(" "); Str(sn.name); Str(" cannot be derived to terminals")
END;
INC(sp)
END;
Texts.Append(Oberon.Log, w.buf)
END TestIfNtToTerm;
PROCEDURE Init*;
BEGIN
maxSet := 0; Sets.Clear(set[0]); Sets.Incl(set[0], eofSy);
firstNt := maxSymbols; maxP := maxSymbols; maxT := -1; maxC := -1;
lastNt := maxP - 1;
dummyName := 0;
nNodes := 0
END Init;
BEGIN (* CRT *)
(* The dummy node gn[0] ensures that none of the procedures
above have to check for 0 indices. *)
nNodes := 0;
gn[0].typ := -1; gn[0].p1 := 0; gn[0].p2 := 0; gn[0].next := 0; gn[0].line := 0;
Texts.OpenWriter(w)
END CRT.